﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace WpfAppHost.TimedQueue
{
    public class TimedQueue<T> 
    {
        public event EventHandler<ItemAvailableEventArgs<T>> ItemAvailableEvent;

        private Thread _thread;
        private Queue<ItemWrapper<T>> _items = new Queue<ItemWrapper<T>>();
        private bool _running = false;
        int _interval = 0, _maxTTL = Int32.MaxValue;
        private AutoResetEvent _pulser = new AutoResetEvent(false);
        object _lock = new object();

        /// <summary>
        /// Creates a queue that serves items with a delay between each one
        /// </summary>
        /// <param name="defaultInterval">Minimum time delay between items being served (ms)</param>
        public TimedQueue(int defaultInterval) : this( defaultInterval, Int32.MaxValue)
        {
        }

        public TimedQueue(int defaultInterval, int maxTTL)
        {
            _interval = defaultInterval;
            _maxTTL = maxTTL;
            _thread = new Thread(Updater);
            _thread.IsBackground = true;
            _thread.Name = "TimedQueue";
        }

        public void AddItem(T item)
        {
            lock (_lock)
            {
                _items.Enqueue(new ItemWrapper<T>(item));
                _pulser.Set();
            }
        }

        public void Start()
        {
            lock (_lock)
            {
                if (!_running)
                {
                    _pulser.Reset();
                    _running = true;
                    _thread.Start();
                }
            }
        }

        public void Stop()
        {
            lock (_lock)
            {
                if (_running)
                {
                    _running = false;
                    _pulser.Set();
                }
            }
        }

        private void Updater(object state)
        {
            ItemWrapper<T> item = default(ItemWrapper<T>);
            while (_running)
            {
                _pulser.WaitOne();

                // Initial check, outside of loop
                lock (_lock)
                {
                    if (_items.Count > 0)
                        item = _items.Dequeue();
                }

                // Repeat while there are items (one pulse may occur with several items ready)
                while (item != null)
                {
                    // If this is an old item, no event is raised.  This could happen if
                    // a process goes into a loop and dumps a large number in a very short
                    // amount of time.  
                    if (DateTime.Now.Subtract(item.TimeStamp).TotalMilliseconds < _maxTTL)
                    {
                        RaiseEvent(item.Item);
                        Thread.Sleep(_interval);
                    }

                    item = default(ItemWrapper<T>);

                    lock (_lock)
                    {
                        if( _items.Count > 0 )
                            item = _items.Dequeue();

                    }
                }
            }
        }

        private void RaiseEvent(T item)
        {
            if (ItemAvailableEvent != null)
            {
                ItemAvailableEventArgs<T> args = new ItemAvailableEventArgs<T>(item);
                ItemAvailableEvent(this, args);
            }
        }
    }
}
